/*
 * File: OrientDBExporter.java 
 *  
 * Created on 23.01.2011
 */
package de.graph.impex;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import com.orientechnologies.orient.core.db.graph.OGraphDatabase;
import com.orientechnologies.orient.core.record.impl.ODocument;

import de.graph.common.Edge;
import de.graph.common.Graph;
import de.graph.common.Vertex;

public class OrientDBExporter extends AbstractImportExport implements Exporter {

    private final OGraphDatabase oGraphDB;

    public OrientDBExporter(String dbName) {
        this(dbName, false);
    }

    public OrientDBExporter(String dbName, boolean forceCleanup) {
        super(dbName, forceCleanup);

        this.oGraphDB = new OGraphDatabase("local:" + dbName);
    }

    public void exportGraph(Graph graph) {
        exportGraph(graph, null);
    }

    public void exportGraph(Graph graph, Vertex root) {

        try {
            oGraphDB.create();

            /*
            oGraphDB.setUseCache(true);
            oGraphDB.declareIntent(new OIntentMassiveInsert());
            oGraphDB.begin(TXTYPE.NOTX);
             */
            
            Map<Vertex, ODocument> vertexOGraphVertexMap = new HashMap<Vertex, ODocument>();

            System.out.println("save graph data: " + new Date());

            // save graph data
            setGraphElement(graph);

            // add root
            if (root != null) {
                ODocument rootDocument = getGraphVertex(vertexOGraphVertexMap, root);

                oGraphDB.setRoot("root", rootDocument);

                rootDocument.save();
            }

            System.out.println("create graph structure: " + new Date());

            // create edges
            int count = 1;
            for (Edge edge : graph.getEdges()) {

                if ((count % 50000) == 0) {
                    System.out.println(" ... " + count + ": " + new Date());
                }

                ODocument sourceOVertex =
                    getGraphVertex(vertexOGraphVertexMap, edge.getSourceVertex());
                ODocument targetOVertex =
                    getGraphVertex(vertexOGraphVertexMap, edge.getTargetVertex());

                // create edge
                getGraphEdge(sourceOVertex, targetOVertex, edge);

                count++;
            }

            /*
            System.out.println("save data: " + new Date());

            for (OGraphVertex oGraphVertex : vertexOGraphVertexMap.values()) {
                oGraphVertex.save();
            }
            */
        }
        finally {
            System.out.println("close DB: " + new Date());

            shutdown();

            System.out.println("done: " + new Date());
        }
    }

    @Override
    protected void shutdown() {
        oGraphDB.close();
    }

    @SuppressWarnings("deprecation")
    private void setGraphElement(Graph graph) {

        ODocument oDocument = oGraphDB.createVertex();

        oDocument.field("IsDirected", graph.isDirected());
        oDocument.field("NodeType", Graph.class.getName());

        oGraphDB.setRoot("graph", oDocument);
    }

    private ODocument getGraphVertex(Map<Vertex, ODocument> vertexDocumentMap, Vertex vertex) {

        ODocument oVertex = vertexDocumentMap.get(vertex);
        if (oVertex != null) {
            return oVertex;
        }

        // create vertex
        oVertex = oGraphDB.createVertex();

        // copy properties
        for (String propertyKey : vertex.getPropertyKeys()) {
            // ignore internal
            if ("inEdges".equals(propertyKey) || "outEdges".equals(propertyKey)) {
                continue;
            }

            Object propertyValue = vertex.getProperty(propertyKey);

            oVertex.field(propertyKey, propertyValue);
        }

        // save at map
        vertexDocumentMap.put(vertex, oVertex);

        // return
        return oVertex;
    }

    private ODocument getGraphEdge(ODocument sourceOVertex, ODocument targetOVertex, Edge edge) {

        ODocument oGraphEdge = oGraphDB.createEdge(sourceOVertex, targetOVertex);

        for (String propertyKey : edge.getPropertyKeys()) {
            // ignore internal
            if ("source".equals(propertyKey) || "target".equals(propertyKey)) {
                continue;
            }

            Object propertyValue = edge.getProperty(propertyKey);

            oGraphEdge.field(propertyKey, propertyValue);
        }

        return oGraphEdge;
    }
}
